package middleware

import (
	"fmt"
	"net/http"
	"vblog/app/token"
	"vblog/app/user"
	"vblog/exception"
	"vblog/ioc"
	"vblog/response"

	"github.com/gin-gonic/gin"
)

type Middleware struct {
	// 基于token认证、鉴权，所以依赖token.service
	tksvc token.Service
	role user.Role
}

func NewMiddleware() *Middleware{
	return &Middleware{
		tksvc: ioc.Controller().Get("token").(token.Service),
	}
}

// 认证
func (m *Middleware) Auth(c *gin.Context) {
	// 1.获取token
	ck, err := c.Cookie(token.TOKEN_COOKIE_NAME)
	if err != nil {
		if err == http.ErrNoCookie {
			response.Fail(c, token.CookieNotFound)
			return
		}
		response.Fail(c, err)
		return
	}

	// 2.校验token
	req := token.NewValiateToken()
	req.AccessToken = ck
	tk, err := m.tksvc.ValiateToken(c.Request.Context(), req)
	if err != nil {
		response.Fail(c, err)
		return
	}

	// 3.传递token到gin的上下文
	if c.Keys == nil {
		c.Keys = map[string]any{}
	}
	c.Keys[token.TOKEN_GIN_KEY_NAME] = tk
}

// 鉴权
func (m *Middleware) Perm(c *gin.Context) {
	// 1.获取认证后的token
	tkobj := c.Keys[token.TOKEN_GIN_KEY_NAME]
	if tkobj == nil {
		response.Fail(c,exception.NewPermissionDeny("token is not found"))
	}

	// 这里断言时，不能把if写在一起，如果写在一起，在2中就不能使用变量v了；go中的作用域规则
	v,ok := tkobj.(*token.Token)
	if ok {
		fmt.Printf("当前登录的用户是%s,角色是%s", v.UserName,v.Role)
	} else {
		response.Fail(c,exception.NewPermissionDeny("token not is *token.Token"))
		return
	}

	// 2.角色判断，实现鉴权
	if v.Role == user.ROLE_ADMIN {
		return
	}

	if v.Role != m.role {
		response.Fail(c,exception.NewPermissionDeny("role is not allow",v.Role))
		return 
	}
}

// 通过闭包，让鉴权中间件携带外部参数
func Required(r user.Role) gin.HandlerFunc {
	a := NewMiddleware()
	a.role = r 
	return a.Perm
}